#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/kfifo.h>

#define BUF_SIZE 64
DEFINE_KFIFO(kbuf,char,BUF_SIZE);
unsigned int remain_size = BUF_SIZE;

struct c0_dev{
	struct device *dev_c0;
	char buf[BUF_SIZE];
};
struct c0_dev *c0;


int c0_open(struct inode *inode,struct file *file)
{
	int major=MAJOR(inode->i_rdev);
	int minor=MINOR(inode->i_rdev);
	printk("%s major : %d, minor : %d\n",__func__,major,minor);
	return 0;
}
int c0_close(struct inode *inode,struct file *file)
{
	printk("%s\n",__func__);
	return 0;
}

ssize_t c0_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{
	unsigned int read_size;
	int ret;
	if(kfifo_is_empty(&kbuf))
	{
		if(filp->f_flags & O_NONBLOCK)
		{
			printk("fifo empty\n");
			remain_size = BUF_SIZE;
			return -EAGAIN;
		}
	}
	ret = kfifo_to_user(&kbuf,buf,count,&read_size);
	if(ret)
		return -EIO;
	*ppos += read_size;
	remain_size += read_size;
	printk("read_size = %d, pos = %lld\n",read_size,*ppos);
	return read_size;
}
ssize_t c0_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
{
	unsigned int written_size;
	int ret;
	if(kfifo_is_full(&kbuf))
	{
		if(filp->f_flags & O_NONBLOCK)
		{
			printk("fifo full\n");
			return -EAGAIN;
		}
	}
	ret = kfifo_from_user(&kbuf,buf,count,&written_size);
	if(ret)
		return -EIO;
	*ppos += written_size;
	remain_size -= written_size; 
	printk("written_size = %d, remain size = %d, pos = %lld\n",written_size,remain_size,*ppos);
	return written_size;
}

const struct file_operations c0_fops={
	.open    = c0_open,
	.read    = c0_read,
	.write   = c0_write,
	.release = c0_close,
};

struct miscdevice misc_c0={
	.minor = MISC_DYNAMIC_MINOR,
	.name  = "misc_c0",
	.fops  = &c0_fops, 
};

static int __init dev_init(void)
{
	int ret;
	c0=(struct c0_dev*)kmalloc(sizeof(struct c0_dev),GFP_KERNEL);
	ret=misc_register(&misc_c0);
	if (ret)
	{
		printk("misc register failed\n");
		return ret;
	}
	c0->dev_c0=misc_c0.this_device;
	printk("register device misc_c0\n");
	return 0;

}

static void __exit dev_exit(void)
{
	printk("----%s----\n",__func__);
	misc_deregister(&misc_c0);
	kfree(c0);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

